package br.mikhas.refractor.field;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import br.mikhas.util.Numbers;
import br.mikhas.util.TypeUtils;

public class UnsafeClassFieldProxy extends ClassFieldProxy {

	/**
	 * The unsafe object
	 */
	private final static Unsafe theUnsafe;

	private final long fieldOffset;

	private Object staticBase;

	static {
		theUnsafe = UnsafeGetter.getUnsafe();
	}

	public UnsafeClassFieldProxy(Class<?> clazz, Field field) {
		super(clazz, field);

		if (Modifier.isStatic(field.getModifiers())) {

			staticBase = theUnsafe.staticFieldBase(field);
			fieldOffset = theUnsafe.staticFieldOffset(field);
		} else {
			fieldOffset = theUnsafe.objectFieldOffset(field);
		}
	}

	@Override
	protected final void internalSet(Object target, Object value) {
		Class<?> type = this.type();

		if (staticBase != null)
			target = staticBase;

		if (TypeUtils.isPrimitive(type)) {

			if (value == null) {

			} else if (Numbers.isNumber(type)) {
				Number number = Numbers.getNumber(type, String.valueOf(value));
				if (type == int.class)
					theUnsafe.putInt(target, this.fieldOffset, number
							.intValue());
				if (type == short.class)
					theUnsafe.putShort(target, this.fieldOffset, number
							.shortValue());
				if (type == long.class)
					theUnsafe.putLong(target, this.fieldOffset, number
							.longValue());
				if (type == byte.class)
					theUnsafe.putByte(target, this.fieldOffset, number
							.byteValue());
				if (type == float.class)
					theUnsafe.putFloat(target, this.fieldOffset, number
							.floatValue());
				if (type == double.class)
					theUnsafe.putDouble(target, this.fieldOffset, number
							.doubleValue());
			} else if (type == boolean.class) {
				theUnsafe.putBoolean(target, this.fieldOffset, Boolean
						.valueOf(String.valueOf(value)));
			} else if (type == char.class) {
				theUnsafe.putChar(target, this.fieldOffset, String.valueOf(
						value).charAt(0));
			}

		} else
			theUnsafe.putObject(target, this.fieldOffset, value);
	}

	@Override
	protected final Object internalGet(Object target) {
		Class<?> type = this.type();
		if (staticBase != null)
			target = staticBase;

		if (TypeUtils.isPrimitive(type)) {

			if (type == int.class)
				return Integer.valueOf(theUnsafe.getInt(target,
						this.fieldOffset));
			else if (type == long.class)
				return Long
						.valueOf(theUnsafe.getLong(target, this.fieldOffset));
			else if (type == short.class)
				return Short.valueOf(theUnsafe.getShort(target,
						this.fieldOffset));
			else if (type == boolean.class)
				return Boolean.valueOf(theUnsafe.getBoolean(target,
						this.fieldOffset));
			else if (type == char.class)
				return Character.valueOf(theUnsafe.getChar(target,
						this.fieldOffset));
			else if (type == double.class)
				return Double.valueOf(theUnsafe.getDouble(target,
						this.fieldOffset));
			else if (type == byte.class)
				return Byte
						.valueOf(theUnsafe.getByte(target, this.fieldOffset));
			else
				return Float.valueOf(theUnsafe.getFloat(target,
						this.fieldOffset));
		} else
			return theUnsafe.getObject(target, this.fieldOffset);
	}
}
